package com.freedom.sql.jdbc.request;

import java.io.IOException;
import java.net.Socket;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.freedom.sql.jdbc.netty.MyNioSocketChannel;
import com.freedom.sql.jdbc.response.LoginOkPacket;
import com.freedom.sql.jdbc.response.ErrorPacket;
import com.freedom.sql.jdbc.utils.ByteUtils;
import com.freedom.sql.jdbc.utils.CapabilitiesUtils;
import com.freedom.sql.jdbc.utils.CharsetUtils;
import com.freedom.sql.jdbc.utils.ErrorCodeUtils;
import com.freedom.sql.jdbc.utils.LoggerUtils;
import com.freedom.sql.jdbc.utils.MyJsonProperties;
import com.freedom.sql.jdbc.utils.SecurityUtil;

import io.netty.buffer.ByteBuf;

public class LoginPacket {
	private static final Logger logger = LogManager.getLogger(LoginPacket.class);
	private MyNioSocketChannel channel;
	private ByteBuf buf;

	public LoginPacket(MyNioSocketChannel c, ByteBuf obj) {
		channel = c;
		buf = obj;
	}

	public void read() throws Exception {
		// 下面开始提取信息
		int bufLen = buf.readableBytes();
		LoggerUtils.debug(logger, "***buf: " + buf + " readable len: " + bufLen);
		// skip长度字节
		buf.skipBytes(3);
		// packet number
		int packetNumber = buf.readByte();
		if (1 != packetNumber) {
			throw new Exception("***the packet Number of login packet is not 1...");
		}
		LoggerUtils.debug(logger, "***packet number---" + packetNumber);
		// 2字节的client capabilities
		int clientCapabilities = buf.readUnsignedShortLE();
		LoggerUtils.debug(logger, "***clientCapabilities is 0X" + Integer.toHexString(clientCapabilities));
		// 2字节的extend client capabilities
		int extendClientCapabilities = buf.readUnsignedShortLE();
		LoggerUtils.debug(logger,
				"***extended clientCapabilities is 0X" + Integer.toHexString(extendClientCapabilities));
		// 4字节的max packet size
		long maxPacketSize = buf.readUnsignedIntLE();
		LoggerUtils.debug(logger, "***max packet size: " + maxPacketSize);
		// 1字节的charset
		int charset = buf.readUnsignedByte();
		String charsetStr = CharsetUtils.getCharset(charset);
		channel.setCharset(charsetStr);
		LoggerUtils.debug(logger, "***charset --- " + charsetStr);
		// 23字节省略
		buf.skipBytes(23);// 必要的话，后面从这里提取extra information
		// user ,以\0结束
		String user = ByteUtils.readZeroTerminatedString(buf);
		LoggerUtils.debug(logger, "***user--" + user);
		// password's len
		int pwdLen = buf.readUnsignedByte();
		byte[] pwd = ByteUtils.readSpecifiedLengthBytes(buf, pwdLen);
		LoggerUtils.debug(logger, "***pwd---" + pwd);
		// database?
		String database = "";
		boolean enable = ((clientCapabilities & CapabilitiesUtils.CLIENT_CONNECT_WITH_DB) != 0);
		boolean remaing = (buf.readableBytes() > 0);
		if (remaing && enable) {
			database = ByteUtils.readZeroTerminatedString(buf);
		}
		LoggerUtils.debug(logger, "***database---" + database);
		// 然后开始验证,// 然后开始判断encryptPass和上文的字节数组
		MyJsonProperties instance = MyJsonProperties.getInstance();
		byte[] encryptPass = SecurityUtil.scramble411(instance.getPassword().getBytes(), channel.getSeed());
		boolean success = instance.getUsername().equals(user) && ByteUtils.compare(encryptPass, pwd);
		LoggerUtils.debug(logger, "***login result: " + success);
		// 然后根据success决定发送什么样的报文
		if (success) {
			channel.setLogined(true);
			new LoginOkPacket(channel).write();
			LoggerUtils.debug(logger, "send AUTH_OK packet succeed...");
		} else {
			String remoteAddress = channel.remoteAddress().getAddress().getHostAddress();
			LoggerUtils.debug(logger, "remote Address:" + remoteAddress);
			// 这个包里面会触发连接关闭操作
			new ErrorPacket(true, channel, 2, ErrorCodeUtils.ER_ACCESS_DENIED_ERROR,
					"Access denied for user '" + user + "'@'" + remoteAddress + "' (using password: YES)", "28000")
							.write();
			LoggerUtils.debug(logger, "send AUTH_FAIL packet succeed...");
		}
	}

	//////////////////////////////////////////////////////
	// 发送login请求
	private int packetSeq;
	private String username;
	private String password;
	private String salt;
	private Socket socket;

	public LoginPacket(Socket _socket, int _packetSeq, String _username, String _passwd, String _salt) {
		socket = _socket;
		this.packetSeq = _packetSeq;
		this.username = _username;
		this.password = _passwd;
		this.salt = _salt;
	}

	public void write() throws IOException {
		// 1)构造3字节的长度
		byte[] headerBytes = null;
		// 2)1字节的packetSeq
		byte[] packetSeqBytes = ByteUtils.writeInt(this.packetSeq + 1, 1);
		// 3) 2字节的client capabilities
		byte[] clientCapabilities = new byte[] { (byte) 0x85, (byte) 0xa6 };
		// 4) 2字节的extend client capabilities
		byte[] extendCapabilities = new byte[] { 0x03, 0x00 };
		// 5) 4字节的max packet size
		byte[] mxPakcetSizeBytes = new byte[] { 0x00, 0x00, 0x00, 0x40 };
		// 6)1字节的charset
		byte[] charsetIndex = new byte[] { 0x21 };
		// 7)23字节
		byte[] blank = ByteUtils.writeInt(0, 23);
		// 8)user，以\0结束
		byte[] usernameBytes = ByteUtils.writeString(this.username);
		// 9)准备密码
		byte[] lengthBytes = new byte[1];
		byte[] passwordBytes = null;
		if (null == password || password.trim().length() == 0) {
			passwordBytes = new byte[0];// 空的
		} else {
			passwordBytes = ByteUtils.passwordCompatibleWithMySQL411(password, salt);
		}
		lengthBytes[0] = (byte) passwordBytes.length;
		// 构造总的长度
		int totalLength = clientCapabilities.length + extendCapabilities.length + mxPakcetSizeBytes.length + 1 + 23
				+ usernameBytes.length + 1 + passwordBytes.length;
		headerBytes = ByteUtils.writeInt(totalLength, 3);
		// 构造总的数据
		socket.getOutputStream().write(headerBytes);
		socket.getOutputStream().write(packetSeqBytes);
		socket.getOutputStream().write(clientCapabilities);
		socket.getOutputStream().write(extendCapabilities);
		socket.getOutputStream().write(mxPakcetSizeBytes);
		socket.getOutputStream().write(charsetIndex);
		socket.getOutputStream().write(blank);
		socket.getOutputStream().write(usernameBytes);
		socket.getOutputStream().write(lengthBytes);
		socket.getOutputStream().write(passwordBytes);
	}
}
